﻿using System.Runtime.Serialization.Json;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;

namespace Lib.Serialization;
/// <summary>
/// 
/// </summary>
public static partial class JavaScript {

    #region "对象序列化成或反序列（XML格式字符串 <=> 对象）"

    /// <summary>
    /// 对象序列化成 XML格式字符串 实体类里属性是 Type 与 Dictionary 类型的要处理后才能序列化
    /// </summary>
    public static string XmlSerialize<T>(this T e, Encoding Code = null) {
        Code ??= Encoding.UTF8;
        try {
            var Xml = new XmlSerializer(typeof(T));
            using var Stream = new MemoryStream();
            Xml.Serialize(Stream, e);
            return Code.GetString(Stream.ToArray());
        } catch {
            return null;
        }
    }
    /// <summary>
    /// XML格式字符串 反序列化成对象
    /// </summary>
    public static T XmlDeserialize<T>(this string e, Encoding Code = null) {
        Code ??= Encoding.UTF8;
        try {
            var Xml = new XmlSerializer(typeof(T));
            using var Stream = new MemoryStream(Code.GetBytes(e));
            using var Reader = XmlReader.Create(Stream);
            return (T)Xml.Deserialize(Reader);
        } catch {
            return default;
        }
    }

    #endregion
    
    #region "XDocument、XmlDocument、XElement 对象序列化 与 XML格式字符串互相转换"

    /// <summary>
    /// 序列化 XDocument 对象
    /// </summary>
    public static string Serialize(this XDocument xml) {
        if (xml == null) {
            return null;
        }
        try {
            return Parse(xml.Root);
        } catch {
            return default;
        }
    }
    /// <summary>
    /// 序列化 XmlDocument 对象
    /// </summary>
    public static string Serialize(this XmlDocument xml) {
        if (xml == null) {
            return null;
        }
        try {
            using var Reader = new XmlNodeReader(xml);
            Reader.MoveToContent(); return Parse(XDocument.Load(Reader).Root);
        } catch {
            return null;
        }
    }
    /// <summary>
    /// 序列化 XElement 对象
    /// </summary>
    public static string Serialize(this XElement xml) {
        if (xml == null) {
            return null;
        }
        try {
            return Parse(xml);
        } catch {
            return null;
        }
    }
    /// <summary>
    /// XML格式字符串 <=> JSon 字符串（互相转换）
    /// </summary>
    public static string XmlToConvert(string Value, string RootKey = null, string RowKey = null, bool Retract = false) {
        if (string.IsNullOrEmpty(Value)) {
            return null;
        }
        try {
            if ((Value.StartsWith('[') && Value.EndsWith(']')) || Value.StartsWith('{') && Value.EndsWith('}')) {
                return Parse(Value, RootKey, RowKey, Retract);
            } else {
                if (Value.StartsWith('<') && Value.EndsWith('>')) {
                    return Parse(XElement.Parse(Value));
                }
            }
        } catch {
            return null;
        }
        return null;
    }

    /// <summary>
    /// 
    /// </summary>
    [GeneratedRegex("^[+-]?\\d*[.]?\\d*$")]
    private static partial Regex RegEx();

    /// <summary>
    /// 解析 XElement 对象
    /// </summary>
    private static string Parse(XElement xml) {

        static string Parse(string Value) {
            if (string.IsNullOrEmpty(Value)) {
                return "null";
            } else {
                if (RegEx().IsMatch(Value) || string.Compare(Value, "false", true) == 0 || string.Compare(Value, "true", true) == 0) {
                    return Value;
                } else {
                    return "\"" + Value + "\"";
                }
            }
        }

        static string Action(XElement Node) {
            var Count = Node.Elements().Count();
            var sb = new StringBuilder();
            var Index = 0;
            if (Node.HasElements) {
                var LocalNames = Node.Elements().Select(T => T.Name.LocalName).Distinct();
                var Number = LocalNames.Count();
                if (Number == 1) {
                    sb.Append('[');
                    foreach (var e in Node.Elements()) {
                        if (Index > 0) {
                            sb.Append(',');
                        }
                        sb.Append(Action(e)); Index += 1;
                    }
                    sb.Append(']');
                } else {
                    sb.Append('{');
                    foreach (var e in Node.Attributes()) {
                        if (e.Name.LocalName == "xsi" || e.Name.LocalName == "xsd") {
                            continue;
                        }
                        if (Index > 0) {
                            sb.Append(',');
                        }
                        sb.AppendFormat("\"{0}\":{1}", e.Name.LocalName, Parse(e.Value)); Index += 1;
                    }
                    foreach (var e in Node.Elements()) {
                        if (Index > 0) {
                            sb.Append(',');
                        }
                        sb.AppendFormat("\"{0}\":{1}", e.Name.LocalName, Action(e)); Index += 1;
                    }
                    sb.Append('}');
                }
            } else {
                if (Node.IsEmpty) {
                    Node.Value = null;
                }
                sb.Append(Parse(Node.Value));
            }
            return sb.ToString();
        }
        return Action(xml);
    }
    /// <summary>
    /// 字符串 => XML格式字符串
    /// </summary>
    private static string Parse(string Value, string RootKey = null, string RowKey = null, bool Retract = false) {
        string Result = null;
        try {
            using var Reader = JsonReaderWriterFactory.CreateJsonReader(Encoding.UTF8.GetBytes(Value), XmlDictionaryReaderQuotas.Max);
            var xml = XElement.Load(Reader);
            if (string.IsNullOrEmpty(RootKey)) {
                xml.Name = "Root";
            } else {
                xml.Name = RootKey;
            }
            if (string.IsNullOrEmpty(RowKey)) {
                RowKey = "Item";
            }
            foreach (var Rs in xml.Descendants()) {
                if (Rs.HasElements && Rs.Name == "item") {
                    Rs.Name = RowKey;
                }
                Rs.RemoveAttributes();
            }
            xml.RemoveAttributes();
            Result = @"<?xml version=""1.0"" encoding=""utf-8""?>";
            if (Retract) {
                Result += "\n" + xml.ToString();
            } else {
                XmlDocument xmlDoc = new();
                xmlDoc.LoadXml(xml.ToString());
                Result += xmlDoc.OuterXml;
            }
        } catch {
            return null;
        }
        return Result;
    }

    #endregion

}